package com.wngbms.netty.seatdemo.message;

import com.wngbms.netty.seatdemo.comm.*;
import io.netty.buffer.ByteBuf;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteBuffer;
import java.util.Arrays;

@Slf4j
public class BaseHeader {

    // 消息头长度: 8Byte
    public static final byte HEADER_LENGTH = 8;

    /**
     * 消息头8字节
     */
    private byte[] header = new byte[HEADER_LENGTH];

    // 可写入的消息体数组索引下标
    private int headerWritableIndex;

    public BaseHeader() {
//        log.info("init header");
    }

    public int writeHeader(byte[] buf, int readableIndex, int readableLength) {
        int min = Math.min(readableLength, HEADER_LENGTH - headerWritableIndex);

        System.arraycopy(buf, readableIndex, header, headerWritableIndex, min);
        headerWritableIndex += min;
        return min;
    }

    public boolean isFinish() {
        return headerWritableIndex == HEADER_LENGTH;
    }

    public void writeHeader(ByteBuf byteBuf) {
        int readableBytes = byteBuf.readableBytes();
        int min = Math.min(readableBytes, HEADER_LENGTH - headerWritableIndex);
//        log.info("writeHeader:{} {} {}", readableBytes, min, headerWritableIndex);
        byteBuf.readBytes(header, headerWritableIndex, min);
        headerWritableIndex += min;
    }

    public void writeHeader(ByteBuffer buffer) {
        buffer.get(header);
    }

    public byte[] getHeader() {
        return header;
    }

    public static BaseHeader build(MagicVersion magicVersion,
                                   CmdType cmdType,
                                   BodyType bodyType,
                                   EncryptionType encryptionType,
                                   ReqOrResp reqOrResp,
                                   int bodyLength) {

        BaseHeader baseHeader = new BaseHeader();
        baseHeader.header[0] = (byte) (magicVersion.getType() << 4
                | cmdType.getType() >> 2);
        baseHeader.header[1] = (byte) (cmdType.getType() << 6
                | bodyType.getType() << 3
                | encryptionType.getType());
        baseHeader.header[2] = (byte) (reqOrResp.getType() << 7);
        baseHeader.header[3] = 0;
        baseHeader.header[4] = (byte) (bodyLength >>> 24);
        baseHeader.header[5] = (byte) (bodyLength >>> 16);
        baseHeader.header[6] = (byte) (bodyLength >>> 8);
        baseHeader.header[7] = (byte) bodyLength;
        return baseHeader;
    }

    public MagicVersion getMagicVersion() {
        byte b0 = header[0];
        return MagicVersion.find((byte) ((b0 & 0xF0) >>> 4));
    }

    public CmdType getCmdType() {
        byte b0 = header[0];
        byte b1 = header[1];
        return CmdType.find((byte) (((b1 & 0xC0) >>> 6 | b0 << 2) & 0x3F));
    }

    public BodyType getBodyType() {
        byte b1 = header[1];
        return BodyType.find((byte) ((b1 & 0x38) >>> 3));
    }

    public EncryptionType getEncryptionType() {
        byte b1 = header[1];
        return EncryptionType.find((byte) (b1 & 0x07));
    }

    public ReqOrResp getReqOrResp() {
        byte b2 = header[2];
        return ReqOrResp.find((byte) (b2 >>> 7));
    }

    public int getBodyLength() {
        return (header[4] & 0xFF) << 24
                | (header[5] & 0xFF) << 16
                | (header[6] & 0xFF) << 8
                | header[7] & 0xFF;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        BaseHeader that = (BaseHeader) o;

        return Arrays.equals(header, that.header);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(header);
    }
}
